package com.mvu.banana.common.widget;

import com.google.gwt.event.dom.client.*;
import com.google.gwt.user.client.ui.*;

public class AutoCompleteTextBox extends TextBox implements KeyUpHandler, ChangeHandler {

  protected PopupPanel choicesPopup = new PopupPanel(true);
  protected ListBox choices = new ListBox();
  protected CompletionItems items = new SimpleAutoCompletionItems(new String[]{});
  protected boolean popupAdded = false;
  protected boolean visible = false;
  private static final int MAX_VISIBLE = 7;

  /**
   * Default Constructor
   */
  public AutoCompleteTextBox() {
    super();
    this.addKeyUpHandler(this);
    choices.addChangeHandler(this);
    choicesPopup.add(choices);
  }

  /**
   * Sets an "algorithm" returning completion items
   * You can define your own way how the textbox retrieves autocompletion items
   * by implementing the CompletionItems interface and setting the according object
   *
   * @param items CompletionItem implementation
   * @see SimpleAutoCompletionItem
   */
  public void setCompletionItems(CompletionItems items) {
    this.items = items;
  }

  /**
   * Returns the used CompletionItems object
   *
   * @return CompletionItems implementation
   */
  public CompletionItems getCompletionItems() {
    return this.items;
  }

  /**
   * A key was released, start autocompletion
   */
  public void onKeyUp(KeyUpEvent event) {
    int code = event.getNativeKeyCode();
    if (code == KeyCodes.KEY_DOWN) {
      int selectedIndex = choices.getSelectedIndex();
      selectedIndex++;
      if (selectedIndex > choices.getItemCount()) {
        selectedIndex = 0;
      }
      choices.setSelectedIndex(selectedIndex);
      return;
    }
    if (code == KeyCodes.KEY_UP) {
      int selectedIndex = choices.getSelectedIndex();
      selectedIndex--;
      if (selectedIndex < 0) {
        selectedIndex = choices.getItemCount();
      }
      choices.setSelectedIndex(selectedIndex);
      return;
    }
    if (code == KeyCodes.KEY_ENTER || code == KeyCodes.KEY_TAB) {
      if (visible) {
        complete();
      }
      return;
    }
    if (code == KeyCodes.KEY_ESCAPE) {
      choices.clear();
      choicesPopup.hide();
      visible = false;
      return;
    }

    String text = this.getText();
    String[] matches = new String[]{};
    if (text.length() > 0) {
      matches = items.getCompletionItems(text, MAX_VISIBLE);
    }

    if (matches.length > 0) {
      choices.clear();
      for (int i = 0; i < matches.length; i++) {
        choices.addItem(matches[i]);
      }
      // if there is only one match and it is what is in the
      // text field anyways there is no need to show autocompletion
      if (matches.length == 1 && matches[0].compareTo(text) == 0) {
        choicesPopup.hide();
      } else {
        choices.setSelectedIndex(0);
        choices.setVisibleItemCount(choices.getItemCount() + 1);

        if (!popupAdded) {
          RootPanel.get().add(choicesPopup);
          popupAdded = true;
        }
        choicesPopup.show();
        visible = true;
        choicesPopup.setPopupPosition(this.getAbsoluteLeft(),
                this.getAbsoluteTop() + this.getOffsetHeight());
        choices.setWidth(this.getOffsetWidth() + "px");
      }

    } else {
      visible = false;
      choicesPopup.hide();
    }
  }

  /**
   * A mouseclick in the list of items
   */
  public void onChange(ChangeEvent arg0) {
    complete();
  }

  // add selected item to textbox
  public void complete() {
    if (choices.getItemCount() > 0) {
      this.setText(choices.getItemText(choices.getSelectedIndex()));
    }
    choices.clear();
    choicesPopup.hide();
  }
}
